Skip to content

Conversation

@SimonVanacco
Copy link

@SimonVanacco SimonVanacco commented Mar 25, 2025

This PR adds support for device code grants, which has been available for some time in oauth2-server

Ready to be tested, I'd love to get some feedback if you think some areas could be improved !

Design considerations

  • I've decided to let end-users handle the device code verification page on their own. This is to prevent having to require twig in the bundle itself and create more complexity to accommodate all use cases. An example controller has been added in the documentation
  • This does not support verification_uri_complete, only verification_uri
  • Logic for code approval is in DeviceCodeRepository::approveDeviceCode. I was unable to re-use the logic from oauth2-server as they fetch the code by "device_code" instead of by "user_code". Not sure if this is a mistake in the parent bundle or some use-case I don't understand, but it does not accommodate most people use-cases. I'm open to moving the logic somewhere else if you find a better place for it !
  • I've added some basic tests but this is not my strong suit : please let me know if I missed something :)

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Can you rebase it? Here are some comments also

SimonVanacco added a commit to SimonVanacco/oauth2-server-bundle that referenced this pull request Apr 26, 2025
@SimonVanacco
Copy link
Author

Hi @chalasr

I'm not entirely sure I understood correctly what you wanted for the docs/device-code-grant.md improvements, but I tried my best to fit to your feedback :)

PR is rebased !

@Mika56
Copy link

Mika56 commented Oct 20, 2025

Interested in this PR, but there's been no activity for a few months. Anything we can do to help?

@Renrhaf
Copy link

Renrhaf commented Oct 21, 2025

+1 interested as well !

Copy link
Contributor

@ajgarlag ajgarlag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work here. I've managed to create a working demo. I've reviewed your code and added some suggestions.

Comment on lines +131 to +134
->scalarNode('device_code_polling_interval')
->info('How soon (in seconds) can the device code be used to poll for the access token without being throttled')
->defaultValue(5)
->end()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have an option to enable verification_uri_complete generation, and another one to enable polling interval visibility in the device authorization response.

Suggested change
->scalarNode('device_code_polling_interval')
->info('How soon (in seconds) can the device code be used to poll for the access token without being throttled')
->defaultValue(5)
->end()
->booleanNode('enable_device_code_verification_uri_complete_generation')
->info('Whether to enable the generation of verification_uri_complete')
->defaultTrue()
->end()
->scalarNode('device_code_polling_interval')
->info('How soon (in seconds) can the device code be used to poll for the access token without being throttled')
->defaultValue(5)
->end()
->booleanNode('enable_device_code_polling_interval_visibility')
->info('Whether to enable the visibility of polling interval')
->defaultTrue()
->end()

Comment on lines +15 to +18
/**
* @param bool $persist Set to true when creating a new device code
*/
public function save(DeviceCodeInterface $deviceCode, bool $persist = true): void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get rid of the boolean argument, like any other manager.

Suggested change
/**
* @param bool $persist Set to true when creating a new device code
*/
public function save(DeviceCodeInterface $deviceCode, bool $persist = true): void;
public function save(DeviceCodeInterface $deviceCode): void;

}

private function buildClientEntity(ClientInterface $client): ClientEntity
public function buildClientEntity(ClientInterface $client): ClientEntity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this changed needed?

Comment on lines +60 to +72
$deviceCode = $this->deviceCodeManager->find($deviceCodeEntity->getIdentifier());
$newDeviceCode = false;

if ($deviceCode) {
if ($deviceCodeEntity->getLastPolledAt()) {
$deviceCode->setLastPolledAt($deviceCodeEntity->getLastPolledAt());
}
} else {
$newDeviceCode = true;
$deviceCode = $this->buildDeviceCodeModel($deviceCodeEntity);
}

$this->deviceCodeManager->save($deviceCode, $newDeviceCode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no need to update the lastPolledAt property. DeviceGrant is already doing it

I think this method should build a new DeviceCode model if not exists (buildDeviceCodeModel), or update it (I would add a updateDeviceCodeModel method) with the values of the received DeviceCodeEntityInterface and finally save it to the DeviceManagerInteface

Comment on lines +127 to +130
->scalarNode('device_code_verification_uri')
->info('The full URI the user will need to visit to enter the user code')
->defaultValue('')
->end()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if we could use a route name here too. If we detect that the value is not an absolute path nor and absolute URL, we could use the URL generator service to generate the URI.

$this->deviceCodeManager->save($deviceCode, $newDeviceCode);
}

public function approveDeviceCode(string $userCode, string $userId): void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether this method should be included in the bundle. I've created a working demo that doesn't use this it.

Either way, I'll move this method to the DeviceCodeManagerInterface and rename it resolveDeviceCode, adding a third required boolean parameter to indicate whether the request has been approved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants